﻿using System;
using System.Configuration;
using System.Net.Http;
using AutoMapper;
using Enferno.Public.InversionOfControl;
using Enferno.Public.Web.Builders;
using Enferno.Public.Web.Components.Server.ViewModels;
using Enferno.Public.Web.Models;
using Enferno.Public.Web.Repositories;
using Enferno.StormApiClient.Customers;
using Enferno.StormApiClient.Expose;
using Enferno.StormApiClient.Shopping;
using Enferno.Web.StormUtils;
using Microsoft.Practices.Unity;

namespace Enferno.Public.Web.Components.Server.Managers
{
    public class CheckoutManager
    {
        private readonly IRepository _repository;
        private readonly CheckoutBuilder _checkoutBuilder;
        private readonly IPaymentDetails _paymentDetails;
        
        public CheckoutManager()
        {
            _repository = IoC.Container.Resolve<IRepository>();
            _checkoutBuilder = new CheckoutBuilder();
            _paymentDetails = IoC.Container.Resolve<IPaymentDetails>();
        }

        public CheckoutViewModelBase GetCheckout()
        {
            if (!StormContext.BasketId.HasValue) return null;

            var checkout = _repository.Shopping.GetCheckout(StormContext.BasketId.Value);
            var checkoutModel = _checkoutBuilder.MapCheckoutModel(checkout);
            return MapCheckoutViewModel(checkoutModel);
        }

        public CheckoutViewModelBase SetPaymentMethod(int basketId, int paymentMethodId)
        {
            var checkout = _repository.Shopping.SetCheckoutPaymentMethod(basketId, paymentMethodId);
            var checkoutModel = _checkoutBuilder.MapCheckoutModel(checkout);
            return MapCheckoutViewModel(checkoutModel);
        }

        public CheckoutViewModelBase SetDeliveryMethod(int basketId, int deliveryMethodId)
        {
            var checkout = _repository.Shopping.SetCheckoutDeliveryMethod(basketId, deliveryMethodId);
            var checkoutModel = _checkoutBuilder.MapCheckoutModel(checkout);
            return MapCheckoutViewModel(checkoutModel);
        }

        public void UpdateOrderReference(int basketId, CheckoutViewModelBase checkoutViewModel)
        {
            var basket = _repository.Shopping.GetBasket(basketId);
            basket.OrderReference = checkoutViewModel.OrderReference;
            _repository.Shopping.UpdateBasket(basket, 1); //TODO: what updatedBy id should be used here?
        }

        public PrivateCheckoutViewModel UpdatePrivateInformation(int basketId, CheckoutBuyerViewModel buyer,
            CheckoutPrivatePayerViewModel payer, CheckoutPrivateShipToViewModel shipTo)
        {
            var buyerModel = Mapper.Map<CheckoutBuyerViewModel, CheckoutBuyerModel>(buyer);

            var shipToModel = Mapper.Map<CheckoutPrivateShipToViewModel, CheckoutPrivateShipToModel>(shipTo);
            var shipToCustomer = Mapper.Map<CheckoutPrivateShipToModel, Customer>(shipToModel);
            var buyerCustomer = Mapper.Map<CheckoutPrivateShipToModel, Customer>(shipToModel);

            var payerModel = Mapper.Map<CheckoutPrivatePayerViewModel, CheckoutPrivatePayerModel>(payer);
            var payerCustomer = Mapper.Map<CheckoutBuyerModel, Customer>(buyerModel);
            Mapper.Map(payerModel, payerCustomer);
            Mapper.Map(payerModel, buyerCustomer);

            Mapper.Map(buyerModel, buyerCustomer);

            _repository.Shopping.UpdateBuyer(basketId, buyerCustomer);
            _repository.Shopping.UpdateShipto(basketId, shipToCustomer);
            var checkout = _repository.Shopping.UpdatePayer(basketId, payerCustomer);

            var checkoutModel = Mapper.Map<Checkout, PrivateCheckoutModel>(checkout);
            return Mapper.Map<PrivateCheckoutModel, PrivateCheckoutViewModel>(checkoutModel);
        }

        public CompanyCheckoutViewModel UpdateCompanyInformation(int basketId, CheckoutBuyerViewModel buyer,
            CheckoutCompanyPayerViewModel payer, CheckoutCompanyShipToViewModel shipTo)
        {
            var buyerModel = Mapper.Map<CheckoutBuyerViewModel, CheckoutBuyerModel>(buyer);

            var shipToModel = Mapper.Map<CheckoutCompanyShipToViewModel, CheckoutCompanyShipToModel>(shipTo);
            var shipToCustomer = Mapper.Map<CheckoutCompanyShipToModel, Customer>(shipToModel);
            var buyerCustomer = Mapper.Map<CheckoutCompanyShipToModel, Customer>(shipToModel);

            var payerModel = Mapper.Map<CheckoutCompanyPayerViewModel, CheckoutCompanyPayerModel>(payer);
            var payerCustomer = Mapper.Map<CheckoutBuyerModel, Customer>(buyerModel);
            Mapper.Map(payerModel, payerCustomer);
            Mapper.Map(payerModel, buyerCustomer);

            Mapper.Map(buyerModel, buyerCustomer);

            _repository.Shopping.UpdateBuyer(basketId, buyerCustomer);
            _repository.Shopping.UpdateShipto(basketId, shipToCustomer); 
            var checkout = _repository.Shopping.UpdatePayer(basketId, payerCustomer);

            var checkoutModel = Mapper.Map<Checkout, CompanyCheckoutModel>(checkout);
            return Mapper.Map<CompanyCheckoutModel, CompanyCheckoutViewModel>(checkoutModel);
        }

        public PaymentResponseViewModel Purchase(int basketId, int selectedPaymentMethodId, string callerIp, string userAgent)
        {
            var paymentParameters = new NameValues();
            if (_paymentDetails != null)
            {
                paymentParameters = _paymentDetails.GetPaymentParameters(selectedPaymentMethodId);
            }

            var paymentResponse = _repository.Shopping.Purchase(basketId, callerIp, userAgent, paymentParameters);
            var paymentResponseModel = Mapper.Map<PaymentResponse, PaymentResponseModel>(paymentResponse);

            if (paymentResponseModel.RedirectUrl == null && paymentResponseModel.Status == PaymentStatus.Ok)
            {
                //we should not do a redirect and the payment status is ok. The purchase has been handled, reset the basket.
                StormContext.ConfirmedBasketId = StormContext.BasketId;
                StormContext.BasketId = null;
            }

            if (paymentResponseModel.RedirectUrl != null)
            {
                //we have a redirect url, set the verb by which to call the redirect url (post or get).
                paymentResponseModel.HttpMethod = GetPaymentRedirectHttpMethod(selectedPaymentMethodId);    
            }

            var viewModel = Mapper.Map<PaymentResponseModel, PaymentResponseViewModel>(paymentResponseModel);

            return viewModel;
        }

        private string GetPaymentRedirectHttpMethod(int selectedPaymentMethodId)
        {
            //set http method depending on what is configured
            var configuredHttpMethod =
                ConfigurationManager.AppSettings[
                    String.Format("PaymentMethodHttpMethod_{0}", selectedPaymentMethodId)];

            if (configuredHttpMethod == null || (configuredHttpMethod.ToLowerInvariant() != "post" && configuredHttpMethod.ToLowerInvariant() != "get"))
            {
                throw new ConfigurationErrorsException(
                    String.Format(
                        "HttpMethod to use for payment method with id {0} is either not configured at all or has an invalid value (get or post is the only supported verbs)",
                        selectedPaymentMethodId));
            }

            return configuredHttpMethod;
        }

        private CheckoutViewModelBase MapCheckoutViewModel(CheckoutModelBase checkoutModel)
        {
            var type = checkoutModel.GetType();
            if (type == typeof (CompanyCheckoutModel))
                return Mapper.Map<CompanyCheckoutModel, CompanyCheckoutViewModel>((CompanyCheckoutModel) checkoutModel);

            if (type == typeof (PrivateCheckoutModel))
                return Mapper.Map<PrivateCheckoutModel, PrivateCheckoutViewModel>((PrivateCheckoutModel) checkoutModel);

            throw new ArgumentException("Checkout model not of correct type", "checkoutModel");
        }
    }
}
